Project: Identify Customer Segments

In this project, you will apply unsupervised learning techniques to identify segments of the population that form the core customer base for a mail-order sales company in Germany. These segments can then be used to direct marketing campaigns towards audiences that will have the highest expected rate of returns. The data that you will use has been provided by our partners at Bertelsmann Arvato Analytics, and represents a real-life data science task.

This notebook will help you complete this task by providing a framework within which you will perform your analysis steps. In each step of the project, you will see some text describing the subtask that you will perform, followed by one or more code cells for you to complete your work. Feel free to add additional code and markdown cells as you go along so that you can explore everything in precise chunks. The code cells provided in the base template will outline only the major tasks, and will usually not be enough to cover all of the minor tasks that comprise it.

It should be noted that while there will be precise guidelines on how you should handle certain tasks in the project, there will also be places where an exact specification is not provided. There will be times in the project where you will need to make and justify your own decisions on how to treat the data. These are places where there may not be only one way to handle the data. In real-life tasks, there may be many valid ways to approach an analysis task. One of the most important things you can do is clearly document your approach so that other scientists can understand the decisions you've made.

At the end of most sections, there will be a Markdown cell labeled Discussion. In these cells, you will report your findings for the completed section, as well as document the decisions that you made in your approach to each subtask. Your project will be evaluated not just on the code used to complete the tasks outlined, but also your communication about your observations and conclusions at each stage.

Step 0: Load the Data

There are four files associated with this project (not including this one):

Each row of the demographics files represents a single person, but also includes information outside of individuals, including information about their household, building, and neighborhood. You will use this information to cluster the general population into groups with similar demographic properties. Then, you will see how the people in the customers dataset fit into those created clusters. The hope here is that certain clusters are over-represented in the customers data, as compared to the general population; those over-represented clusters will be assumed to be part of the core userbase. This information can then be used for further applications, such as targeting for a marketing campaign.

To start off with, load in the demographics data for the general population into a pandas DataFrame, and do the same for the feature attributes summary. Note for all of the .csv data files in this project: they're semicolon (;) delimited, so you'll need an additional argument in your read_csv() call to read in the data properly. Also, considering the size of the main dataset, it may take some time for it to load completely.

Once the dataset is loaded, it's recommended that you take a little bit of time just browsing the general structure of the dataset and feature summary file. You'll be getting deep into the innards of the cleaning in the first major step of the project, so gaining some general familiarity can help you get your bearings.

I can see that dataset AZDIAS_Feature_Summary presents basic information about data in columns of dataset Udacity_AZDIAS_Subset.

Tip: Add additional cells to keep everything in reasonably-sized chunks! Keyboard shortcut esc --> a (press escape to enter command mode, then press the 'A' key) adds a new cell before the active cell, and esc --> b adds a new cell after the active cell. If you need to convert an active cell to a markdown cell, use esc --> m and to convert to a code cell, use esc --> y.

Step 1: Preprocessing

Step 1.1: Assess Missing Data

The feature summary file contains a summary of properties for each demographics data column. You will use this file to help you make cleaning decisions during this stage of the project. First of all, you should assess the demographics data in terms of missing data. Pay attention to the following points as you perform your analysis, and take notes on what you observe. Make sure that you fill in the Discussion cell with your findings and decisions at the end of each step that has one!

Step 1.1.1: Convert Missing Value Codes to NaNs

The fourth column of the feature attributes summary (loaded in above as feat_info) documents the codes from the data dictionary that indicate missing or unknown data. While the file encodes this as a list (e.g. [-1,0]), this will get read in as a string object. You'll need to do a little bit of parsing to make use of it to identify and clean the data. Convert data that matches a 'missing' or 'unknown' value code into a numpy NaN value. You might want to see how much data takes on a 'missing' or 'unknown' code, and how much data is naturally missing, as a point of interest.

As one more reminder, you are encouraged to add additional cells to break up your analysis into manageable chunks.

FIRSTLY, BEFORE I take care for the missing values, I would like to see (and present in the notebook) columns in two categories:

a) categorical and ordinal columns - and for each of them to show all unique values in the form of **pie chart** b) continuous-valued columns - and for each of them print **histogram**. **I know that information about the type of data in the columns and the individual values that represent the missing values for each column is in the Summary file, but I want to make sure and do this analysis by myself.**

I'm not strongly sure what's behind these criteria, so I'll take a sample for previewing...

For finded examples of columns, I'll print some values that are inside of them, but also, outside of this notebook, I'll inspect "Data_Dictionary.md" file for detailed information and better understanding.

Here I was fairly sure, because the name "categorical" makes it clear how we should treat this data. In the above-printed samples, I see digital labels in int and float formats. I was expecting string formats but you can't judge that there are no such columns either. The "categorical" columns will be converted to one-hot-encoder.

The name "ordinal" also clearly identifies the data in the columns. These are categorical data, but such that there are dependencies and ranks between different values. Therefore, for such data, I will keep the original value numbering (at least at the stage of data cleansing, before scaling).

"Numeric" columns are a combination of continuous values and discrete but strictly numerical values such as year (1974, 1999 etc.). In order not to complicate the work too much, I will not divide this set into actual continuous and ordinal data, but will treat all of them as if they were continuous data. It follows from this decision that when I further inspect the contents of the columns, I will not print or graph the values unique to those columns. To find missing or incorrect values, I will only look for anything that is not a number or for "-1" if it is so indicated by the sources.

I'll rather treat data in columns "mixed" as categorical data, but confirm that after visual inspection of values in those columns.

There is only one column with 'interval' data, and I've learned from "Data_Dictionary" file, that these values are discreet numeric values for birthdate intervals. I'll treat that column as "ordinal" data.

So, as I wrote above, I would like to split the dataset columns into two groups:

a) "categorical" and "ordinal" columns

b) and columns with continuous numeric values


Based on the review above and in line with the categories from AZDIAS_Feature_Summary.csv:

a) == 'categorical', 'ordinal', 'mixed', 'interval'

b) == 'numeric'

Now I will print the missing values given in the Summary file for each column.

Looking at the pie charts, I HAVE NOTICED other missing values or incorrect values than those marked in the Summary file! For example: for columns like KBA05_ANTG1 to KBA05_ANTG4 there should be "-1" value for missing_unknown, as it states in summary above. Yet, looking at the pie chart for those columns, we can't see such a value, but instead "null" value exists. In this particular case it's not a problem, because I'll overwrite every missing_unknown value to NaN anyway. However, as they say, caution is never enough and I feel more confident after doing my own analysis.

Now I'll show the diagrams after missing/unknown data parsing to NaNs.

Step 1.1.2: Assess Missing Data in Each Column

How much missing data is present in each column? There are a few columns that are outliers in terms of the proportion of values that are missing. You will want to use matplotlib's hist() function to visualize the distribution of missing value counts to find these columns. Identify and document these columns. While some of these columns might have justifications for keeping or re-encoding the data, for this project you should just remove them from the dataframe. (Feel free to make remarks about these outlier columns in the discussion, however!)

For the remaining features, are there any patterns in which columns have, or share, missing data?

Discussion 1.1.2: Assess Missing Data in Each Column

After drawing the diagram above, at first glance, I see 3 ranges of the number of missing values:

Due to the significant lack of data, I removed the following columns:

Step 1.1.3: Assess Missing Data in Each Row

Now, you'll perform a similar assessment for the rows of the dataset. How much data is missing in each row? As with the columns, you should see some groups of points that have a very different numbers of missing values. Divide the data into two subsets: one for data points that are above some threshold for missing values, and a second subset for points below that threshold.

In order to know what to do with the outlier rows, we should see if the distribution of data values on columns that are not missing data (or are missing very little data) are similar or different between the two groups. Select at least five of these columns and compare the distribution of values.

Depending on what you observe in your comparison, this will have implications on how you approach your conclusions later in the analysis. If the distributions of non-missing features look similar between the data with many missing values and the data with few or no missing values, then we could argue that simply dropping those points from the analysis won't present a major issue. On the other hand, if the data with many missing values looks very different from the data with few or no missing values, then we should make a note on those data as special. We'll revisit these data later on. Either way, you should continue your analysis for now using just the subset of the data with few or no missing values.

This is an optimistic info to see, that almost 70% of rows are full (no missing values).

Discussion 1.1.3: Assess Missing Data in Each Row

For the following pairs, I find these distributions rather similar to each other. Moreover, the part of the data with rows below the threshold point contains more data than the part with rows above the threshold point. The data with lots of missing values are NOT qualitatively different (in most cases) from data with few or no missing values. In my opinion, rows with more than 9 missing data can be rejected before further analysis.

Step 1.2: Select and Re-Encode Features

Checking for missing data isn't the only way in which you can prepare a dataset for analysis. Since the unsupervised learning techniques to be used will only work on data that is encoded numerically, you need to make a few encoding changes or additional assumptions to be able to make progress. In addition, while almost all of the values in the dataset are encoded using numbers, not all of them represent numeric values. Check the third column of the feature summary (feat_info) for a summary of types of measurement.

In the first two parts of this sub-step, you will perform an investigation of the categorical and mixed-type features and make a decision on each of them, whether you will keep, drop, or re-encode each. Then, in the last part, you will create a new data frame with only the selected and engineered columns.

Data wrangling is often the trickiest part of the data analysis process, and there's a lot of it to be done here. But stick with it: once you're done with this step, you'll be ready to get to the machine learning parts of the project!

Step 1.2.1: Re-Encode Categorical Features

For categorical data, you would ordinarily need to encode the levels as dummy variables. Depending on the number of categories, perform one of the following:

Discussion 1.2.1: Re-Encode Categorical Features

Step 1.2.2: Engineer Mixed-Type Features

There are a handful of features that are marked as "mixed" in the feature summary that require special treatment in order to be included in the analysis. There are two in particular that deserve attention; the handling of the rest are up to your own choices:

Be sure to check Data_Dictionary.md for the details needed to finish these tasks.

Discussion 1.2.2: Engineer Mixed-Type Features

I replaced old mixed columns: PRAEGENDE_JUGENDJAHRE and CAMEO_INTL_2015 with new ones, splitting data they described into 4 new columns: PJ_DECADE, PJ_MOVEMENT, CI_WEALTH, CI_LIFESTAGE.

I dropped LP_LEBENSPHASE_FEIN and LP_LEBENSPHASE_GROB, because it's hard to split the features they describe.

The rest of mixed columns (WOHNLAGE, PLZ8_BAUMAX) I treat as an ordinal columns, because they describes single features, they are not really 'mixed' features in my opinion.

Step 1.2.3: Complete Feature Selection

In order to finish this step up, you need to make sure that your data frame now only has the columns that you want to keep. To summarize, the dataframe should consist of the following:

Make sure that for any new columns that you have engineered, that you've excluded the original columns from the final dataset. Otherwise, their values will interfere with the analysis later on the project. For example, you should not keep "PRAEGENDE_JUGENDJAHRE", since its values won't be useful for the algorithm: only the values derived from it in the engineered features you created should be retained. As a reminder, your data should only be from the subset with few or no missing values.

Step 1.3: Create a Cleaning Function

Even though you've finished cleaning up the general population demographics data, it's important to look ahead to the future and realize that you'll need to perform the same cleaning steps on the customer demographics data. In this substep, complete the function below to execute the main feature selection, encoding, and re-engineering steps you performed above. Then, when it comes to looking at the customer data in Step 3, you can just run this function on that DataFrame to get the trimmed dataset in a single step.

Step 2: Feature Transformation

Step 2.1: Apply Feature Scaling

Before we apply dimensionality reduction techniques to the data, we need to perform feature scaling so that the principal component vectors are not influenced by the natural differences in scale for features. Starting from this part of the project, you'll want to keep an eye on the API reference page for sklearn to help you navigate to all of the classes and functions that you'll need. In this substep, you'll need to check the following:

Discussion 2.1: Apply Feature Scaling

For all columns (except 'numeric') I used the most common value method (filling by mode). It is one of the simpler methods when we want to quickly and manually fill in the gaps (as opposed to machine learning tools, e.g. kNN). For numeric columns, I used median filling to disrupt the distribution of the variables as little as possible.

Scaling changes the feature values so that each column is treated by the model with the same priority. Scaling prevents high-valued features being considered more important, than features, which values are naturaly smaller. After scaling, all features are treated equally.

Step 2.2: Perform Dimensionality Reduction

On your scaled data, you are now ready to apply dimensionality reduction techniques.

Discussion 2.2: Perform Dimensionality Reduction

I chose 125 principal components as they cover more than 90% of the variance.

Step 2.3: Interpret Principal Components

Now that we have our transformed principal components, it's a nice idea to check out the weight of each variable on the first few components to see if they can be interpreted in some fashion.

As a reminder, each principal component is a unit vector that points in the direction of highest variance (after accounting for the variance captured by earlier principal components). The further a weight is from zero, the more the principal component is in the direction of the corresponding feature. If two features have large weights of the same sign (both positive or both negative), then increases in one tend expect to be associated with increases in the other. To contrast, features with different signs can be expected to show a negative correlation: increases in one variable should result in a decrease in the other.

Discussion 2.3: Interpret Principal Components

At the outset, I must point out that the weights obtained indicate that no Principal Component is strongly correlated with any feature from the original data set. The highest absolute value of the correlation is only 0.3397 for ANREDE_KZ and 3rd Principal Component and it is not a high value.


(PCA1) First Principal Component Analysis

Description of the first and last 3 features:

LP_STATUS_GROB_1.0 - social status (the more - the more wealthy)

PLZ8_ANTG3 - number of 6-10 family houses in the region (the more - the higher number)

CI_WEALTH - level of how wealthy is a household (increase - poorer)

(4th feature, for making sure) HH_EINKOMMEN_SCORE - estimated household net income (the more - the lower income)


PLZ8_ANTG1 - number of 1-2 family houses in the region (the more - the higher number)

FINANZ_MINIMALIST - low financial interest (the more points - the more true)

MOBI_REGIO - movement patterns (from highiest to lowest)


On the basis of the first three and the last three features, I conclude that this component is a measure of the financial status, financial awareness and environmental factors in the region. However, I am somewhat consterned by the fact that two features: LP_STATUS_GROB_1.0 and CI_WEALTH are mutually correlated, although the description of their value contradicts this (the higher the value of LP, the better the material position AND the higher the value of CI, the worse the material position). The explanation for this may be a wrong description of the LP scale or the fact that one of them may apply to a single person and the other - to the entire household. The next, fourth feature (HH_EINKOMMEN_SCORE) confirms the CI_WEALTH feature, so I will consider it as the correct correlation.

Generally:

smaller PCA1 value =

greater PCA1 =




(PCA2) Second Principal Component Analysis

Description of the first and last 3 features:

ALTERSKATEGORIE_GROB - Estimated age based on given name analysis (the more - the older)

FINANZ_VORSORGER - stable money status, has money, but not investor yet (the more points - the more true)

ZABEOTYP_3 - Energy consumption typology (the less - the more "green", the more - the more indifferent to the topic)


SEMIO_REL - religious (the more points - the less affinity)

FINANZ_SPARER - money-saver (the more points - the more true)

PJ_DECADE - dominating decade of person's youth (the more - the later years)


The Second Principal Component is a measure of a person's age, the way they manage their money and the values they follow (environmental protection, economy, religiosity).

Generally:

smaller PCA1 value =

greater PCA1 =




(PCA3) Third Principal Component Analysis

Description of the first and last 3 features:

SEMIO_VERT - dreamful (the more points - the less affinity)

SEMIO_FAM - family-minded (the more points - the less affinity)

SEMIO_SOZ - socially-minded (the more points - the less affinity)


SEMIO_DOM - dominant-minded (the more points - the less affinity)

SEMIO_KAEM - combative attitude (the more points - the less affinity)

ANREDE_KZ - gender (male 1, female 2)


Third Principal Component is a measure of personality typology.

Generally:

smaller PCA1 value =

greater PCA1 =

Step 3: Clustering

Step 3.1: Apply Clustering to General Population

You've assessed and cleaned the demographics data, then scaled and transformed them. Now, it's time to see how the data clusters in the principal components space. In this substep, you will apply k-means clustering to the dataset and use the average within-cluster distances from each point to their assigned cluster's centroid to decide on a number of clusters to keep.

Discussion 3.1: Apply Clustering to General Population

The plot from MiniBatch is less smooth than the plot from regular KMeans, because computations and results arre obtained in the series of batches, which makes the single error results more unstable. However, calculations are performed much much (!) faster, which is a significant advantage.

At the plot I can see no clear point for so-called 'elbow'. I subjectively selected point 12th as the one to which the error drops significantly. So I decided to segment the population into 12 clusters on the basis of the scree plot above. Behind that point, the error drops slower. I think that chosing 12 clusters is the most accommodating decision in this case.

Step 3.2: Apply All Steps to the Customer Data

Now that you have clusters and cluster centers for the general population, it's time to see how the customer data maps on to those clusters. Take care to not confuse this for re-fitting all of the models to the customer data. Instead, you're going to use the fits from the general population to clean, transform, and cluster the customer data. In the last step of the project, you will interpret how the general population fits apply to the customer data.

Step 3.3: Compare Customer Data to Demographics Data

At this point, you have clustered data based on demographics of the general population of Germany, and seen how the customer data for a mail-order sales company maps onto those demographic clusters. In this final substep, you will compare the two cluster distributions to see where the strongest customer base for the company is.

Consider the proportion of persons in each cluster for the general population, and the proportions for the customers. If we think the company's customer base to be universal, then the cluster assignment proportions should be fairly similar between the two. If there are only particular segments of the population that are interested in the company's products, then we should see a mismatch from one to the other. If there is a higher proportion of persons in a cluster for the customer data compared to the general population (e.g. 5% of persons are assigned to a cluster for the general population, but 15% of the customer data is closest to that cluster's centroid) then that suggests the people in that cluster to be a target audience for the company. On the other hand, the proportion of the data in a cluster being larger in the general population than the customer data (e.g. only 2% of customers closest to a population centroid that captures 6% of the data) suggests that group of persons to be outside of the target demographics.

Take a look at the following points in this step:

Discussion 3.3: Compare Customer Data to Demographics Data

The above diagrams show that the top three bars for Population and the top three bars for Customers correspond to the same clusters: clusters no. 4, 9 and 12 (in ticks: 3, 8, 11). Thus, the company's 3 most important target groups coincide with the three most numerous groups in the population. A large target audience is a greater likelihood of high income for the company. This distribution of customer groups is promising. However, there is room for improvement. Namely, the group with the lowest share among customers is claster 11 (tick 10 on the charts). For the population, however, cluster 11 is consecutively the fourth most numerous. Also, clusters 7 and 8 are under-represented among customers.


About question 1: "What kinds of people are part of a clusters that are overrepresented in the customer data compared to the general population?"


Cluster 4 (tick 3)

FEATURE Mean Median Mode What the feature describes What the value tells (characteristic of people in cluster)
PJ_MOVEMENT 0.974209 1.0 1.0 movement of person's youth avant-garde
GREEN_AVANTGARDE 0.974209 1.0 1.0 if membership in environmental sustainability as part of youth yes, green avant-garde member
LP_STATUS_GROB_5.0 0.850048 1.0 1.0 if social status = top earners yes
LP_STATUS_FEIN_9.0 0.001850 0.0 0.0 if social status = houseowner no
LP_STATUS_GROB_4.0 0.002229 0.0 0.0 if social status = houseowner (double feature) no
WOHNLAGE 3.054167 3.0 3.0 neighborhood quality average neighborhood

Generally, cluster 4 is mainly related to people growing up in the time of avant-garde, involved in the green movement, with very high incomes, living in an average neighborhood.

Cluster 9 (tick 8)

FEATURE Mean Median Mode What the feature describes What the value tells (characteristic of people in cluster)
OST_WEST_KZ 0.067668 -0.0 -0.0 Building location via former East / West Germany (GDR / FRG) West (FRG)
CI_LIFESTAGE 3.270293 3.0 4.0 life stage typology Older Families & Mature Couples
ARBEIT 2.242643 2.0 2.0 Share of unemployment in community low
PLZ8_GBZ 3.881307 4.0 4.0 Number of buildings within the region 300-449 buildings
PLZ8_HHZ 3.382820 3.0 3.0 Number of households within the PLZ8 region 300-599 households
KBA13_ANZAHL_PKW 699.099667 638.0 536.0 Number of cars in the region around 640

Cluster 9 is mainly associated with people living in West Germany (former FRG) belonging to the group of older families or mature couples, living in a community of low unemployment rate, rather in a village or a small town (based on number of houses, number of households and number of cars).

Cluster 12 (tick 11)

FEATURE Mean Median Mode What the feature describes What the value tells (characteristic of people in cluster)
CI_LIFESTAGE 2.836338 3.0 1.0 life stage typology Pre-Family Couples & Singles
CAMEO_DEUG_2015_6 0.228653 -0.0 0.0 if low-consumption middleclass no
CAMEO_DEUG_2015_3 0.054887 0.0 -0.0 if established middleclass no
LP_FAMILIE_FEIN_2.0 0.262084 0.0 -0.0 if couple (as status of relationship) no
CAMEO_DEU_2015_7A 0.054762 0.0 0.0 if journeymen no
CAMEO_DEUG_2015_7 0.166220 -0.0 -0.0 if lower middleclass no

Cluster 12 is mainly associated with people who are single, not low-consumption middleclass or established middleclass, not journeymen and not belonging to the lower middleclass.





About question 2: "What kinds of people are part of a clusters that are underrepresented in the customer data compared to the general population?"


Cluster 11 (tick 10)

FEATURE Mean Median Mode What the feature describes What the value tells (characteristic of people in cluster)
LP_FAMILIE_GROB_2.0 0.104953 0.0 0.0 if family type = couple no
LP_FAMILIE_FEIN_2.0 0.104953 0.0 0.0 if couple (as status of relationship) no
LP_STATUS_GROB_3.0 0.028302 0.0 -0.0 if social status = independents no
KBA05_ANTG4 0.706368 0.0 0.0 Number of 10+ family houses in the microcell no any
LP_STATUS_GROB_2.0 0.053066 0.0 0.0 if social status = average earners no
LP_FAMILIE_GROB_4.0 0.083726 0.0 -0.0 if family type = single family no

The selected most correlated features in this case say who the people in cluster 11 ARE NOT. People are not in an informal relationship (but this does not mean that they are not in any relationship at all), they are not average earners (they can earn less or more), are not a single family in a household and do not have multi-family buildings in their neighborhood (+10 families).

Cluster 7 (tick 6)

FEATURE Mean Median Mode What the feature describes What the value tells (characteristic of people in cluster)
GEBAEUDETYP_3.0 0.423174 -0.0 -0.0 if type of building = mixed (=residential and company) building no
LP_STATUS_GROB_3.0 0.053736 0.0 0.0 if social status = independents no
LP_STATUS_FEIN_6.0 0.021830 0.0 0.0 if social status = independent workers no
CAMEO_DEUG_2015_6 0.109992 0.0 -0.0 if low-consumption middleclass no
GEBAEUDETYP_RASTER 3.109152 3.0 3.0 Ratio of residential to commercial activity mixed cell with middle business share
GEBAEUDETYP_1.0 0.314861 -0.0 0.0 if type of building = residential building no

The selected most correlated features in this case say who the people in cluster 11 ARE NOT. People are not living in mixed buildings (residental and company), are not independent workers, are not low-consumption middleclass, and ARE living in the area of middle business share.

Cluster 8 (tick 7)

FEATURE Mean Median Mode What the feature describes What the value tells (characteristic of people in cluster)
LP_FAMILIE_GROB_4.0 0.069121 -0.0 -0.0 if family type = single family no
LP_STATUS_FEIN_3.0 0.060105 -0.0 0.0 if social status = aspiring low-income earners no
LP_FAMILIE_FEIN_7.0 0.027799 -0.0 -0.0 if family with teenager no
LP_FAMILIE_FEIN_10.0 0.190083 0.0 -0.0 if two-generational household no
CAMEO_DEUG_2015_9 0.018032 0.0 0.0 if urban working class no
LP_FAMILIE_GROB_5.0 0.301277 0.0 0.0 if family type = multiperson household no

The selected most correlated features in this case say who the people in cluster 11 ARE NOT. People are not living as a single full family, are not aspiring low-income earners, are not family with teenager, are not living in a two-generational household, are not urban working class and are not living in a multiperson household.




Underrepresented clusters's most corelated features do not make it clear to which groups of people they apply. The specification based on them is not clear and intuitive. A much more intuitive grouping can be seen in the case of overrepresented clusters, which described above.

Congratulations on making it this far in the project! Before you finish, make sure to check through the entire notebook from top to bottom to make sure that your analysis follows a logical flow and all of your findings are documented in Discussion cells. Once you've checked over all of your work, you should export the notebook as an HTML document to submit for evaluation. You can do this from the menu, navigating to File -> Download as -> HTML (.html). You will submit both that document and this notebook for your project submission.